import 'dart:convert';

import '../extension.dart';
import 'm3u8_utils.dart';

class M3u8Segemnt {
  final String inf;
  final double duration;
  final String rawUrl;

  Uri? fullUri(Uri m3u8Uri) => M3u8Utils.toFullUri(rawUrl, m3u8Uri);

  M3u8Segemnt(this.inf, this.duration, this.rawUrl);
}

class M3u8Encryption {
  static const methodPrefix = 'METHOD=';
  static const uriPrefix = 'URI=';
  static const ivPrefix = 'IV=';
  final String method;
  final String uri;
  final String? iv;

  Uri? fullUri(Uri m3u8Uri) => M3u8Utils.toFullUri(uri, m3u8Uri);

  @override
  String toString() {
    String str = 'METHOD=$method,URI="$uri"';
    if (iv != null) {
      str += ',IV=$iv';
    }
    return str;
  }

  static M3u8Encryption? parse(String content) {
    final pairs = content.split(',');
    String method = '';
    String uri = '';
    String? iv;

    for (final pair in pairs) {
      if (pair.startsWith(methodPrefix)) {
        method = pair.substring(methodPrefix.length);
      } else if (pair.startsWith(uriPrefix)) {
        uri = pair.substring(uriPrefix.length).trimString('"');
      } else if (pair.startsWith(ivPrefix)) {
        iv = pair.substring(ivPrefix.length);
      }
    }
    if (method.isEmpty) return null;
    return M3u8Encryption(method: method, uri: uri, iv: iv);
  }

  M3u8Encryption({required this.method, required this.uri, this.iv});
}

class M3u8File {
  static const _extKey = '#EXT-X-KEY:';
  static const _extInf = '#EXTINF:';
  static const _extStart = '#EXTM3U';
  static const _extEndList = '#EXT-X-ENDLIST';
  M3u8Encryption? encryption;
  final List<M3u8Segemnt> segments;
  final List<String> extXLines;
  double? _duration;

  M3u8File({this.encryption, required this.segments, required this.extXLines});

  double get duration {
    if (_duration == null) {
      double total = .0;
      for (final s in segments) {
        total += s.duration;
      }
      _duration = total;
    }
    return _duration!;
  }

  static M3u8File? parse(String content) {
    M3u8Encryption? encryption;
    final segments = <M3u8Segemnt>[];
    final iterator = LineSplitter.split(content).iterator;
    final extXLines = <String>[];
    bool hasStart = false;
    bool hasEnd = false;
    while (iterator.moveNext()) {
      final line = iterator.current.trim();
      if (line.isEmpty) continue;
      if (line.startsWith(_extStart)) {
        hasStart = true;
      }
      if (line.startsWith('#EXT-X-')) {
        extXLines.add(line);
      }
      if (line.startsWith(_extKey)) {
        encryption = M3u8Encryption.parse(line.substring(_extKey.length));
        if (encryption == null) return null;
        continue;
      } else if (line.startsWith(_extInf)) {
        final inf = line.substring(_extInf.length);
        final infSegs = inf.split(',');
        if (infSegs.isEmpty) return null;
        final duration = double.tryParse(infSegs[0]);
        if (duration == null) return null;
        final ok = iterator.moveNext();
        if (!ok) return null;
        segments.add(M3u8Segemnt(inf, duration, iterator.current));
        continue;
      } else if (line.startsWith(_extEndList)) {
        hasEnd = true;
        break;
      }
    }
    if (!hasStart) return null;
    if (!hasEnd) return null;
    return M3u8File(
      encryption: encryption,
      segments: segments,
      extXLines: extXLines,
    );
  }

  @override
  String toString() {
    StringBuffer sb = StringBuffer();
    sb.writeln(_extStart);
    for (String line in extXLines) {
      if (line.startsWith(_extKey)) {
        if (encryption == null) {
          assert(false, '有key行,encryption 为null');
        } else {
          line = '$_extKey${encryption.toString()}';
        }
      } else if (line.startsWith(_extEndList)) {
        continue;
      }
      sb.writeln(line);
    }
    for (final seg in segments) {
      sb.writeln('$_extInf${seg.inf}');
      sb.writeln(seg.rawUrl);
    }
    sb.writeln(_extEndList);
    return sb.toString();
  }
}
